﻿using System;
using System.Runtime.InteropServices;

namespace HDM.NexusEngine.Core
{
    /// <summary>
    /// Used to represent a position in 3D space. 
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct Vector
    {
        public static Vector Zero = new Vector(0, 0, 0);
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public Vector(double x, double y, double z) : this()
        {
            X = x;
            Y = y;
            Z = z;
        }

        public double Length()
        {
            return Math.Sqrt(LengthSquared());
        }

        public double LengthSquared()
        {
            return (X*X + Y*Y + Z*Z);
        }

        public static Vector Normalize(Vector v)
        {
            var r = v.Length();

            // guard against divide by zero
            return r!=0.0 ? new Vector(v.X/r, v.Y/r, v.Z/r) : new Vector(0, 0, 0);
        }

        public bool Equals(Vector v)
        {
            return (X == v.X) && (Y == v.Y) && (Z == v.Z);
        }

        public static bool operator ==(Vector v1, Vector v2)
        {
            // If they're the same object or both null, return true.
            if (ReferenceEquals(v1, v2)) return true;
            // If one is null. but not both, return false/
            if (v1 == null || v2 == null) return false;

            return v1.Equals(v2);
        }

        public override bool Equals(object obj)
        {
            if (obj is Vector) return Equals((Vector) obj);
            return base.Equals(obj);
        }

        public static bool operator !=(Vector v1, Vector v2)
        {
            return !v1.Equals(v2);
        }

        public Vector Add(Vector r)
        {
            return new Vector(X + r.X, Y + r.Y, Z + r.Z);
        }

        public static Vector operator +(Vector v1, Vector v2)
        {
            return v1.Add(v2);
        }

        public Vector Subtract(Vector r)
        {
            return new Vector(X - r.X, Y - r.Y, Z - r.Z);
        }

        public static Vector operator -(Vector v1, Vector v2)
        {
            return v1.Subtract(v2);
        }

        public Vector Multiply(double v)
        {
            return new Vector(X*v, Y*v, Z*v);
        }

        public static Vector operator *(Vector v, double s)
        {
            return v.Multiply(s);
        }

        public double DotProduct(Vector v)
        {
            return (v.X*X) + (Y*v.Y) + (Z*v.Z);
        }

        public static double operator *(Vector v1, Vector v2)
        {
            return v1.DotProduct(v2);
        }

        public Vector CrossProduct(Vector v)
        {
            double nx = Y*v.Z - Z*v.Y;
            double ny = Z*v.X - X*v.Z;
            double nz = X*v.Y - Y*v.X;
            return new Vector(nx, ny, nz);
        }

        public override int GetHashCode()
        {
            return (int) X ^ (int) Y ^ (int) Z;
        }

        public override string ToString()
        {
            return string.Format("X:{0}, Y:{1}, Z:{2}", X, Y, Z);
        }
    }
}